package bancosys.tec.services.implementor.descriptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Collection;

import jmine.tec.di.metadata.DependencyMetaData;
import bancosys.tec.services.implementor.annotations.visitor.DependencyMetadataParser;

/**
 * @author MarceloT
 */
public final class InstanceCreatorDescriber {

    private final DependencyMetadataParser dependencyMetadataParser;

    /**
     * C'tor
     * 
     * @param dependencyMetadataParser {@link DependencyMetadataParser}
     */
    public InstanceCreatorDescriber(DependencyMetadataParser dependencyMetadataParser) {
        this.dependencyMetadataParser = dependencyMetadataParser;
    }

    /**
     * Cria um creator
     * 
     * @param type {@link Class}
     * @return {@link InstanceCreator}
     */
    public InstanceCreator findCreator(Class<?> type) {
        Constructor<?>[] constructors = type.getConstructors();
        if (constructors.length == 0) {
            throw new IllegalArgumentException(type + " doesn't have public constructors!");
        }
        Constructor<?> ctor = this.findValidConstructor(constructors);
        return this.creatorForConstructor(type, ctor);
    }

    /**
     * Cria um {@link InstanceCreator}
     * 
     * @param type o tipo
     * @param ctor o construtor
     * @return {@link InstanceCreator}
     */
    public InstanceCreator creatorForConstructor(Class<?> type, Constructor<?> ctor) {
        DependencyMetaData[] resolvers = this.dependenciesForConstructor(ctor);
        return new InstanceCreatorImpl(ctor, resolvers);
    }

    /**
     * Devolve as dependencias para o construtor passado
     * 
     * @param ctor o construtor
     * @return {@link DependencyMetaData}
     */
    public DependencyMetaData[] dependenciesForConstructor(Constructor<?> ctor) {
        if (ctor == null) {
            throw new IllegalArgumentException("no valid constructor found.");
        }
        Collection<? extends DependencyMetaData> collection =
                this.dependencyMetadataParser.getClassReader().visitConstructorParametersAnnotation(ctor);
        return collection.toArray(new DependencyMetaData[collection.size()]);
    }

    /**
     * Procura um construtor valido
     * 
     * @param constructors os construtores
     * @return {@link Constructor}
     */
    public Constructor<?> findValidConstructor(Constructor<?>[] constructors) {
        for (Constructor<?> constructor : constructors) {
            if (!constructor.isSynthetic() && this.isValidConstructor(constructor)) {
                return constructor;
            }
        }
        return null;
    }

    /**
     * @param ctor o {@link Constructor}
     * @return boolean
     */
    private boolean isValidConstructor(Constructor<?> ctor) {
        int mod = ctor.getModifiers();
        if (!Modifier.isPublic(mod)) {
            return false;
        }
        Annotation[][] parameterAnnotations = ctor.getParameterAnnotations();
        return AbstractServiceAnnotationParser.isValidInjectionParameter(parameterAnnotations);
    }

}
